<!doctype html>
<html>
	<head>
		<meta charset="utf-8">
		<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">

		<title>reveal.js</title>

		<link rel="stylesheet" href="css/reveal.css">
		<link rel="stylesheet" href="css/theme/black.css">

		<!-- Theme used for syntax highlighting of code -->
		<link rel="stylesheet" href="lib/css/zenburn.css">

		<!-- Printing and PDF exports -->
		<script>
			var link = document.createElement( 'link' );
			link.rel = 'stylesheet';
			link.type = 'text/css';
			link.href = window.location.search.match( /print-pdf/gi ) ? 'css/print/pdf.css' : 'css/print/paper.css';
			document.getElementsByTagName( 'head' )[0].appendChild( link );
		</script>
	</head>
	<body>
		<div class="reveal">
			<div class="slides" data-notes="Something important">


<section>
	<section data-markdown>
		# ES6
		- let & const、变量提升、暂时死区（TDZ）
		- 延展符
		- 解构赋值
		- 模板字符串
		- 函数
		- Generator
		- 可迭代协议
		- Map/WeakMap(Set)
		- Promise & async function、macro task & micro task
		- Proxy
		- Reflect
	</section>
</section>

<section>
	<section data-markdown>
		# let & const
		ES6 新增了let和const命令，用来声明变量。它的用法类似于var，但是所声明的变量，只在let命令所在的代码块内有效。
```javascript
{
	var a = 1;
	let b = 2; 
}

console.log(a); // 1
console.log(b); // ReferenceError
[run]
```
	</section>
	<section data-markdown>
		### 关于变量提升
```javascript
console.log(a); // undefined
var a = 1;

console.log(b); // ReferenceError
let b = 2;
[run]
```
	</section>
	<section data-markdown>
```javascript
var a = 1;
{
	console.log(a); // ReferenceError
	let a = 1;
}
[run]
```
	</section>
	<section data-markdown>
		<script type="text/template">
			### MDN上的解释
			旧：
			<img src="img/let_old.png" />
			新：
			<img src="img/let_new.png" />
		</script>
	</section>
	<section data-markdown>
		### 值得读一下  
		https://zhuanlan.zhihu.com/p/28140450
		https://stackoverflow.com/questions/31219420/are-variables-declared-with-let-or-const-not-hoisted-in-es6/31222689
	</section>
</section>

<section>
	<section data-markdown>
		# 延展符
```javascript
let arr1 = ['a', 'b'];
let ar2 = [1, 2, ...arr1]; // [ 1, 2, 'a', 'b']

function f (val, ...a) {
	return val + a.length
}
f(1, 'a', 'b'); // 3
f(1, ...arr1); // 3

let str = "foo"
let chars = [ ...str ] // [ "f", "o", "o" ]
```
	</section>
	<section data-markdown>
		### 对象的延展符（es2018/es9）
```javascript
let obj1 = { foo: 'bar', x: 42 };
let obj2 = { foo: 'baz', y: 13 };

let mergedObj = { ...obj1, ...obj2 }; // { foo: "baz", x: 42, y: 13 }
```
	</section>
	<section data-markdown>
		<script type="text/template">
			### redux推荐使用对象延展符
			<img src="img/redux_objSpread.png" style="width: 90vh" />
		</script>
	</section>
	<section data-markdown>
		<script type="text/template">
			### babel已默认支持对象延展符
			<img src="img/babel.png" />
		</script>
	</section>
</section>

<section>
	<section data-markdown>
		# 解构赋值
```javascript
let {a, b: val1, d = 'dddd'} = {a: 'aa', b: 'bb', c: 'cc'};
console.log(a, val1, d); // aa bb dddd
let {obj, obj: {a1, a2}} = {obj: {a1: 'a111', a2: 'a222'}};
console.log(obj, a1, a2); // {a1: "a111", a2: "a222"} a111 a222


let [b1, , b2, b3 = 4] = [1, 2, 3];
console.log(b1, b2, b3); // 1 3 4
let [c1, [c2, c3]] = [1, [2, 3]];
console.log(c1, c2, c3); // 1 2 3
[run]
```
	</section>
	<section data-markdown>
			### 解构赋值时使用延展符
```javascript
let {a, ...others} = {a: 1, b: 2, c: 3};
console.log(a, others); // 1 {b: 2, c: 3}

let [val1, ...otherVals] = [1, 2, 3];
console.log(val1, otherVals); // 1 [2, 3]
[run]
```
	</section>
</section>

<section>
	<section data-markdown>
		# 模板字符串
```javascript
let customer = { name: "Foo" };
let card = { amount: 7, product: "Bar", unitprice: 42 };
let message = `Hello ${customer.name},
want to buy ${card.amount} ${card.product} for
a total of ${card.amount * card.unitprice} bucks?`;

console.log(message)
/**
Hello Foo,
want to buy 7 Bar for
a total of 294 bucks?
*/
[run]
```
	</section>
	<section data-markdown>
		### 带标签的模板字符串
```javascript
let a = 'aaaa';
let b = 10;

function fn(arr, v1, v2) {
  console.log(arr); // ["lalala ", " yayaya ", ""]
  console.log(v1); // aaaa
  console.log(v2); // 10
}

fn`lalala ${a} yayaya ${b}`;
fn(['lalala ', ' yayaya ', ''], a, b);
[run]
```
  </section>
  <section data-markdown>
    例子：过滤 HTML 字符串，防止用户输入恶意内容
<script type="text/template"><pre><code class="javascript" data-trim>
	let sender = `&lt;script&gt;alert("abc")&lt;/script&gt;`; // 恶意代码
	document.write(`&lt;p&gt;${sender} has sent you a message.&lt;/p&gt;`);
	let message = SaferHTML`&lt;p&gt;${sender} has sent you a message.&lt;/p&gt;`;
	document.write(message);
	
	function SaferHTML(templateData) {
		let s = templateData[0];
		for (let i = 1; i < arguments.length; i++) {
			let arg = String(arguments[i]);
	
			// Escape special characters in the substitution.
			s += arg.replace(/&amp;/g, "&amp;").replace(/&lt;/g, "&lt;").replace(/&gt;/g, "&gt;");
	
			// Don't escape special characters in the template.
			s += templateData[i];
		}
		return s;
	}
</code></pre></script>
	</section>
</section>

<section>
	<section data-markdown>
		# 函数
		### 默认值
```javascript
function plus(a = 1, b = 1) {
	return a + b;
}
console.log(plus(5)) // 6

let value = 10;
function plus2(a = 1, b = value + 1) {
	return a + b;
}
console.log(plus2(5)) // 16

value = 100;
console.log(plus2(5)) // 106
[run]
```
	</section>
	<section data-markdown>
```javascript
function fetch1(url, { body = '', method = 'GET', headers = {} }) {
	console.log(body, method);
}

fetch1('http://example.com', {body: '123'}) // 123 GET

fetch1('http://example.com') // 报错

function fetch2(url, { body = '', method = 'GET', headers = {} } = {}) {
	console.log(body, method);
}

fetch2('http://example.com') // GET
[run]
```
	</section>
	<section data-markdown>
		### 箭头函数
```javascript
let f = v => v;
let f = function (v) {return v;};

var f = () => 5;
var f = function () { return 5; };

var sum = (num1, num2) => num1 + num2;
var sum = function(num1, num2) {return num1 + num2;};

var sum = (num1, num2) => {return num1 + num2;};

let fn = () => void doesNotReturn();
```
	</section>
	<section data-markdown>
		### 箭头函数的this   
```javascript
function Person1() {
	this.age = 24;
	setTimeout(function growUp() {
		console.log(this.age)
	}, 0);
}
new Person1();

function Person2() {
	this.age = 24;
	setTimeout(function growUp() {
		console.log(this.age)
	}.bind(this), 0);
}
new Person2();

function Person3() {
	this.age = 24;
	setTimeout(() =>  {
		console.log(this.age)
	}, 0);
}
new Person3();
[run]
```
	</section>
</section>

<section>
	<section data-markdown>
		# Generator
```javascript
function* gen() { 
	yield 1;
	yield 2;
	yield 3;
}

let g = gen(); // "Generator { }"
console.log(g.next().value); // 1
console.log(g.next().value); // 2
console.log(g.next().value); // 3
console.log(g.next().value); // undefined
[run]
```
	</section>
	<section data-markdown>
```javascript
function* anotherGenerator(i) {
	yield i + 1;
	yield i + 2;
	yield i + 3;
}

function* generator(i){
	yield i;
	yield* anotherGenerator(i); // 移交执行权
	yield i + 10;
}

var gen = generator(10);

console.log(gen.next().value); // 10
console.log(gen.next().value); // 11
console.log(gen.next().value); // 12
console.log(gen.next().value); // 13
console.log(gen.next().value); // 20
[run]
```
	</section>
</section>

<section>
	<section data-markdown>
		# for...of
		for...of语句在可迭代对象（包括 Array，Map，Set，String，TypedArray，arguments 对象等等）上创建一个迭代循环，调用自定义迭代钩子，并为每个不同属性的值执行语句
```javascript
let iterable = [10, 20, 30];

for (let value of iterable) {
	console.log(value);
}
[run]
```
	</section>
</section>

<section>
	<section data-markdown>
		<script type="text/template">
			# 可迭代协议
			可迭代协议允许 JavaScript 对象去定义或定制它们的迭代行为, 例如（定义）在一个 for..of 结构中什么值可以被循环得到。   
			为了变成可迭代对象， 一个对象必须实现 **@@iterator** 方法, 意思是这个对象（或者它原型链 prototype chain 上的某个对象）必须有一个名字是 **Symbol.iterator** 的属性:   
			<table>
				<thead>
					<tr>
						<th>属性</th>
						<th>值</th>
					</tr>
			</thead>
				<tbody>
					<tr>
						<td>[Symbol.iterator]	</td>
						<td>返回一个对象的无参函数，被返回对象符合迭代器协议。</td>
					</tr>
				</tbody>
			</table>
		</script>
	</section>
	<section data-markdown>
		<script type="text/template">
			# 迭代器协议
			该迭代器协议定义了一种标准的方式来产生一个有限或无限序列的值。   
			当一个对象被认为是一个迭代器时，它实现了一个 next() 的方法并且拥有以下含义：  
			<table>
				<thead>
					<tr>
						<th>属性</th>
						<th>值</th>
					</tr>
			</thead>
				<tbody>
					<tr >
						<td>next</td>
						<td>
							返回一个对象的无参函数，被返回对象拥有两个属性：
							<ul>
								<li>
									done (boolean)
									<ul>
										<li>如果迭代器已经经过了被迭代序列时为 true。</li>
										<li>如果迭代器可以产生序列中的下一个值，则为 false。这等效于连同 done 属性也不指定。</li>
									</ul>
								</li>
								<li>
									value - 迭代器返回的任何 JavaScript 值。done 为 true 时可省略。
								</li>
							</ul>
						</td>
					</tr>
				</tbody>
			</table>
		</script>
	</section>
	<section data-markdown>
<script type="text/template"><pre><code class="javascript" data-trim style="max-height: 500px;">
let obj = {};    
obj[Symbol.iterator] = function() {
	return {
		next: function() {
			if (this._count < 4) {
				return { value: this._count++, done: false };
			} else {
				return { done: true };
			}
		},
		_count: 1
	};
};

let itr = obj[Symbol.iterator]();
console.log(itr.next().value); // 1
console.log(itr.next().value); // 2
console.log(itr.next().value); // 3
console.log(itr.next().value); // undefined

for(let val of obj) console.log(val); // 1 2 3

console.log([...obj]); // [1, 2, 3]
[run]
</code></pre></script>
  </section>
  <section data-markdown>
```javascript
var obj = {};
obj[Symbol.iterator] = function* () {
	yield 1;
	yield 2;
	yield 3;
};

let itr = obj[Symbol.iterator]();
console.log(itr.next().value); // 1
console.log(itr.next().value); // 2
console.log(itr.next().value); // 3
console.log(itr.next().value); // undefined

for(let val of obj) console.log(val); // 1 2 3

console.log([...obj]); // [1, 2, 3]
[run]
```
	</section>
	<section data-markdown>
		# 用于可迭代对象的语法
		一些语句和表达式是预料会用于可迭代对象，比如 for-of 循环，延展符, yield* 和 解构赋值。
```javascript
for(let value of ["a", "b", "c"]){
	console.log(value); // a, b, c
}

console.log([..."abc"]); // ["a", "b", "c"]

function* gen(){
	yield* ["a", "b", "c"];
}
console.log(gen().next()); // { value:"a", done:false }

let [a, b, c] = new Set(["a", "b", "c"]);
console.log(a, b, c); // "a" "b" "c"
[run]
```
	</section>
</section>

<section>
	<section data-markdown>
		# Map
```javascript
const m = new Map([
	[1, 'one'],
	[2, 'two'],
]);
m.set('lala', '123');
const keyObj1 = {};
const keyObj2 = {};
const keyFunc = function(){};
m.set(keyObj1, 'obj1');
m.set(keyObj2, 'obj2');
m.set(keyFunc, 'funcfucnfunc');

m.delete(2);

console.log(m);
console.log(m.get(1)); // one
console.log(m.get(keyObj1)); // obj1
[run]
```
	</section>
	<section data-markdown>
		# Set
```javascript
const set = new Set([1,2]);

set.add(5);
set.add(5);
set.add('some text');
set.delete(2);

console.log(set);
console.log(set.has(1)); // true
[run]
```
	</section>
	<section data-markdown>
		# WeakMap
		WeakMap 对象是一组键/值对的集合，其中的键是弱引用的。其键必须是对象，而值可以是任意的。  
		WeakMap 每个键对自己所引用对象的引用是 "弱引用"，这意味着，如果没有其他引用和该键引用同一个对象，这个对象将会被当作垃圾回收
```javascript
let objKey = {};
let m = new Map();
m.set(objKey, 123);

objKey = null;
```
```javascript
let objKey2 = {};
let wm = new WeakMap();
wm.set(objKey2, 123);

objKey2 = null;
```
	</section>
	<section data-markdown>
		### nodejs 试验
```javascript
global.gc();
console.log(process.memoryUsage().heapUsed);

let map = new WeakMap();
let key = new Array(5 * 1024 * 1024);
map.set(key, 1);
global.gc();
console.log(process.memoryUsage().heapUsed); 

key = null;
global.gc();
console.log(process.memoryUsage().heapUsed); 
```
	</section>
	<section data-markdown>
		### 在 dom 对象上存储数据
```javascript
let wm = new WeakMap();
let ele = document.querySelector(".lalala");
wm.set(ele, "123");

ele.parentNode.removeChild(ele);
ele = null;
```
	</section>
  <section data-markdown>
    ### 私有属性
```
const private = new WeakMap();
class Person {
  constructor(name) {
		private.set(this, {name});
	}

  setName(name) {
		private.get(this).name = name;
	}

  getName() {
		return private.get(this).name;
  }
}

export default Person;
```
https://juejin.im/post/5b852d51f265da43351d6580
	</section>
</section>

<section>
	<section data-markdown>
  # Promise & async function
```
const promise1 = new Promise(function(resolve, reject) {
	setTimeout(function() {
		resolve('lalala');
	}, 1000);
});

promise1.then(value => console.log(value));
[run]
```
```
const promise1 = new Promise(function(resolve, reject) {
	setTimeout(function() {
		resolve('lalala');
	}, 1000);
});
async function asyncFunc() {
	let result = await promise1;
	console.log(result);
}

asyncFunc();
[run]
```
	</section>
	<section data-markdown>
```
setTimeout(function() {console.log(5)}, 0);
new Promise((resolve) => {
  console.log(1);
  resolve();
  console.log(2);
}).then(function() {
  console.log(4);
});
console.log(3);
[run]
```
	</section>
	<section data-markdown>
		https://github.com/creeperyang/blog/issues/21
		https://nodejs.org/en/docs/guides/event-loop-timers-and-nexttick/
		https://jsblog.insiderattack.net/event-loop-and-the-big-picture-nodejs-event-loop-part-1-1cb67a182810
		https://zhuanlan.zhihu.com/p/35039878
		https://cnodejs.org/topic/57d68794cb6f605d360105bf
	</section>
</section>

<section>
	<section data-markdown>
# Proxy
Proxy 对象用于定义基本操作的自定义行为（如属性查找，赋值，枚举，函数调用等）
```
let p = new Proxy(target, handler);
```
	</section>
	<section data-markdown>
```
const obj = new Proxy({}, {
  set: function(target, prop, val){
    if (Number.isInteger(val)) target[prop] = val;
    else throw new TypeError('lalala');
  },
  get: function(target, prop){
    return target.hasOwnProperty(prop) ? target[prop] : 0;
  },
  deleteProperty: function(target, prop) {
    if (target[prop] < 100) delete target[prop];
  }
});

obj.a = 1;

console.log(obj); // Proxy {a: 1}
console.log(obj.a); // 1
console.log('b' in obj, obj.b); // false, 0
delete obj.a // property 'a' removed
console.log(obj); // Proxy {}
[run]
```
	</section>
	<section data-markdown>
```
handler.get // obj.foo
handler.set // obj.foo = 1
handler.has // "foo" in obj
handler.construct // new func()
handler.deleteProperty // delete obj.foo
handler.ownKeys // Object.keys(obj)、Object.getOwnPropertyNames(obj)
handler.getOwnPropertyDescriptor // Object.getOwnPropertyDescriptor(obj, "foo")
handler.defineProperty // Object.defineProperty(obj, "foo", {})
handler.getPrototypeOf // Object.getPrototypeOf(obj)、obj.__proto__
handler.setPrototypeOf // Object.setPrototypeOf(obj)
handler.preventExtensions // Object.preventExtensions(obj)
handler.isExtensible // Object.isExtensible(obj)
handler.apply // 当目标对象为函数，且被调用时触发。
```
		</section>
</section>

<section>
	<section data-markdown>
# Reflect
- 将Object对象的一些明显属于语言内部的方法（比如Object.defineProperty），放到Reflect对象上
- 用一个单一的全局对象去存储这些方法，能够保持其它的JavaScript代码的整洁、干净
- 将一些命令式的操作如delete，in等使用函数来替代，这样做的目的是为了让代码更加好维护，更容易向下兼容
	</section>
	<section data-markdown>
```
Reflect.apply // Function.prototype.apply
Reflect.construct // new
Reflect.defineProperty // Object.defineProperty
Reflect.deleteProperty // Object.deleteProperty
Reflect.get // obj.foo
Reflect.set // obj.foo = 1
Reflect.has // in
Reflect.getOwnPropertyDescriptor //  Object.getOwnPropertyDescriptor
Reflect.getPrototypeOf // Object.getPrototypeOf
Reflect.setPrototypeOf // Object.setPrototypeOf
Reflect.isExtensible // Object.isExtensible
Reflect.ownKeys // Object.getOwnPropertyNames(obj).concat(Object.getOwnPropertySymbols(obj))
Reflect.preventExtensions // Object.preventExtensions
```
	</section>
	<section data-markdown>
```
let num = 1;
console.log(num.a);
console.log(Reflect.get(num, 'a')); // TypeError
[run]
```
```
let num = 1;
num.a = 123;
Reflect.set(num, 'a', 123); // TypeError
[run]
```
	</section>
</section>


  		</div>
		</div>

		<script src="lib/js/head.min.js"></script>
		<script src="js/reveal.js"></script>

		<script>
			Reveal.initialize({
				dependencies: [
					{ src: 'plugin/markdown/marked.js' },
					{ src: 'plugin/markdown/markdown.js' },
					{ src: 'plugin/notes/notes.js', async: true },
					{ src: 'plugin/highlight/highlight.js', async: true, callback: function() { hljs.configure({tabReplace: '  '}); hljs.initHighlightingOnLoad(); } }
				]
			});
		</script>

		<script>
			// 实现文中代码的运行功能（eval运行），匹配末尾为“[run]”的
			const runCode = function() {
				const code = this.parentNode.innerHTML
					.replace(/(<span.*?>)|(<\/span>)|(<div.*div>)/g, '')
					.replace(/&amp;/g, "&").replace(/&lt;/g, "<")
					.replace(/&gt;/g, ">");
				eval(code);
			}
			let setBtnInterval = setInterval(() => {
				let codeEles = document.querySelectorAll('code.hljs');
				if (codeEles.length > 0) {
					clearInterval(setBtnInterval);
					[...codeEles].forEach((code) => {
						let innerHTML = code.innerHTML;
						if (innerHTML.includes('[run]')){
							let btn = document.createElement('div');
							btn.className = 'runBtn';
							btn.textContent = 'RUN';
							btn.addEventListener('click', runCode);
							code.innerHTML = innerHTML.replace(/\s+\[run\]/, '').replace(/  /g, ' ');
							code.appendChild(btn);
						} else {
							code.innerHTML = innerHTML.replace(/  /g, ' ');
						}
					});
				}
			}, 50);
		</script>

		<!-- 自定义的样式 -->
		<style rel="stylesheet">
			.reveal h1 {
				font-size: 3.1rem;
				text-transform: none;
			}
			.reveal h2 {
				font-size: 2.5rem;
				text-transform: none;
			}
			.reveal h3 {
				font-size: 2rem;
				text-transform: none;
			}
			.reveal p,ul {
				font-size: 1.6rem !important;
				line-height: 2.2rem !important;
				text-align: left !important;
			}
			.reveal p > img {
				margin: 0 auto;
				display: block;
			}
			.hljs::-webkit-scrollbar {
				width: 0px;
				background: transparent;
			}
			.reveal table th, .reveal table td {
				padding: .5rem;
				font-size: 1.7rem;
			}
			.reveal table th {
				white-space: nowrap;
			}
			code {
				font-size: 1.2rem !important;
				line-height: 1.6rem !important;
				tab-size: 2;
				max-height: 500px !important;
			}
			code > .runBtn {
				position: absolute;
				bottom: 5px;
				right: 5px;
				font-size: 14px;
				line-height: 25px;
				color: white;
				padding: 0 15px;
				background: transparent;
				border: 1px solid white;
				border-radius: 3px;
				cursor: pointer;
			}
		</style>
	</body>
</html>
